﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace ProjectNocturnalFool
{
    //helper class here
    #region Row Helper Class
    /// <summary>
    /// Contains a row of Tiles.
    /// </summary>
    class TileRow
    {
        public List<Tile> columns = new List<Tile>();
    }
    #endregion


    /// <summary>
    /// Contains all the rows of Tiles.
    /// </summary>
    class TileSet
    {

        #region Fields

        public List<TileRow> rows = new List<TileRow>();
        public int tileSetWidth = 19;
        public int tileSetHeight = 27;
        #endregion

        #region Constructor
        /// <summary>
        /// constructor
        /// </summary>
        public TileSet()
        {
            for (int y = 0; y < tileSetHeight; y++)
            {
                TileRow thisRow = new TileRow();
                for (int x = 0; x < tileSetWidth; x++)
                {
                    thisRow.columns.Add(new Tile(0));
                }
                rows.Add(thisRow);
            }

            //HACK Create Sample TileSet Data
            Point testPt = new Point(1,3);
            getTile(testPt).tileImageID = 10;
            getTile(getUpR(testPt, 1)).tileImageID = 10;
            getTile(getUpR(testPt, 2)).tileImageID = 10;
            getTile(getUpR(testPt, 3)).tileImageID = 10;
            

            rows[2].columns[1].AddHeightTileImage(54);
        //    rows[4].columns[1].AddHeightTileImage(54);
            rows[7].columns[2].AddHeightTileImage(54);
            //rows[4].columns[2].AddHeightTileImage(54);
            rows[7].columns[2].AddHeightTileImage(54);
        }

        #endregion

        #region Methods

        public void Update(GameTime gameTime) {
            for (int i = 0; i < rows.Count; i++) {
                for (int y = 0; y < rows[i].columns.Count; y++) {
                    rows[i].columns[y].Update(gameTime);
                }
            }
        }


        public float getDrawDepth(int row, int col, int altitude)
        {
            float maxDepth = ((GameLogic.Instance.gameBoardLogic.tiles.tileSetWidth + 1) * ((GameLogic.Instance.gameBoardLogic.tiles.tileSetHeight + 1) * Constants.tileImageW)) * 10;

            return (0.7f - ((col + (row * Constants.tileImageW)) / maxDepth)) - altitude*Constants.heightRowDepthMod;
        }


        //HACK
        public void changeImage(int _c, int _r)
        {
            if( (_r >= 0 && _c >= 0) && (_r<=tileSetHeight && _c<=tileSetWidth) )
            {
                rows[_r].columns[_c].tileImageID = 10;
            }
        }

        /// <summary>
        /// Returns the number of height tile images on a given Tile(x,y)
        /// </summary>
        /// <param name="_c">column</param>
        /// <param name="_r">row</param>
        /// <returns></returns>
        public int getAltitude(int _c, int _r) {
            if (_r < 0 || _c < 0 || _r >= tileSetHeight || _c >= tileSetWidth) { return 0; }
          
            return rows[_r].columns[_c].getHeight();
        }


        /// <summary>
        /// Determines what the real row of a visible point is
        /// Results to be used along with sprite
        /// </summary>
        /// <param name="_c">column</param>
        /// <param name="_r">row</param>
        /// <returns></returns>
        public int getRealTileRow(int _c, int _r) {
            int rr = _r;
            for (int y = _r; y+1 < tileSetHeight; y+=2) {
                if ((y - getAltitude(_c, y)*2 <= rr)
                   &&(y - getAltitude(_c, y)*2 <= _r)
                )
                {
                    rr = y;
                }
            }

            return rr;
        }

        public Tile getTile(int _c, int _r) {
            return rows[_r].columns[_c];
        }

        public Tile getTile(Point p) {
            return rows[p.Y].columns[p.X];
        }

        public int getTerrainCost(Point p) {
            return getTile(p).terrainCost;
        }

        public bool inBounds(Point p) {
            if (p.X >= 0 && p.X < tileSetWidth && p.Y >= 0 && p.Y < tileSetHeight)
                return true;
            else
                return false;
        }

        public int altitudeDifference(Point p1, Point p2) {
            return Math.Abs(getAltitude(p1.X, p1.Y) - getAltitude(p2.X, p2.Y));
        }

        /// <summary>
        /// Method for navigating tiles more easily
        /// </summary>
        /// <param name="_r"></param>
        /// <param name="_c"></param>
        /// <param name="d"></param>
        /// <returns></returns>
        public Point getUpR(Point s, int d){
            Point ret;
            ret.X = 0;
            ret.Y = 0;

            ret.Y = s.Y - d;


            if (Math.Abs(d) % 2 == 0)
            {
                ret.X = s.X + (d / 2);
            }
            else if (Math.Abs(s.Y) % 2 == 1)
            {
                ret.X = s.X + ((d + 1) / 2);
            }
            else
            {
                ret.X = s.X + ((d - 1) / 2);
            }

            
            return ret;
        }

        //counterpart to getUpR
        public Point getUpL(Point s, int d)
        {
            Point ret;
            ret.X = 0;
            ret.Y = 0;

            ret.Y = s.Y - d;


            if (Math.Abs(d) % 2 == 0)
            {
                ret.X = s.X - (d / 2);
            }
            else if (Math.Abs(s.Y) % 2 == 1)
            {
                ret.X = s.X - ((d - 1) / 2);
            }
            else
            {
                ret.X = s.X - ((d + 1) / 2);
            }


            return ret;
        }

        public Point getDownR(Point s, int d) { return getUpL(s, -d); }
        public Point getDownL(Point s, int d) { return getUpR(s, -d); }

        public void deselect() {
            for (int i = 0; i < rows.Count; i++)
            {
                for (int y = 0; y < rows[i].columns.Count; y++)
                {
                    rows[i].columns[y].mouseSelected=false;
                    //rows[i].columns[y].availableSelection = false;
                    //rows[i].columns[y].inAreaOfEffect = false;
                }
            }
        }

        public void calculateMoveOptions()
        {
            Point unitPt = GameLogic.Instance.gameBoardLogic.entities.actingEntity().onTile;
            //GameLogic.Instance.gameBoardLogic.tiles.getTile(unitPt).availableSelection = true;
            //List<Tile> mTiles = new List<Tile>();
            //m
            //mTiles.Add(GameLogic.Instance.gameBoardLogic.tiles.getTile(unitPt));

            findValidNeighbours(unitPt, Constants.entityAP);

            //for(int i =0; i<mTiles.Count; i++){
              //  mTiles[i].availableSelection = true;
            //}
            
        }

        private void findValidNeighbours(Point cp, int mv) {
            //checks if this tile is too tough to move onto
            if (mv < 0) { return; }
            else { getTile(cp).availableSelection = true;}

            //checks if there's an easier/as easy route to this point
            if (getTile(cp).currentMoveCost > Constants.entityAP - mv)
            {getTile(cp).currentMoveCost = Constants.entityAP - mv;}
            else
            {return;}

            //checks if range is used up
            if (mv == 0) { return; }

            //gets neighbouring points and recursively checks them
            Point ul = getUpL(cp,1);
            Point ur = getUpR(cp,1);
            Point dl = getDownL(cp,1);
            Point dr = getDownR(cp,1);
            if (inBounds(ul) && altitudeDifference(ul, cp) < 2) 
                findValidNeighbours(ul, mv - getTerrainCost(ul));
            if (inBounds(ur) && altitudeDifference(ur, cp) < 2)
                findValidNeighbours(ur, mv - getTerrainCost(ur));
            if (inBounds(dl) && altitudeDifference(dl, cp) < 2)
                findValidNeighbours(dl, mv - getTerrainCost(dl));
            if (inBounds(dr) && altitudeDifference(dr, cp) < 2)
                findValidNeighbours(dr, mv - getTerrainCost(dr));

        }

        public void stopMovement()
        {
            for (int i = 0; i < rows.Count; i++)
            {
                for (int y = 0; y < rows[i].columns.Count; y++)
                {
                    //rows[i].columns[y].mouseSelected = false;
                    rows[i].columns[y].availableSelection = false;
                    getTile(y, i).currentMoveCost = 999;
                    //rows[i].columns[y].inAreaOfEffect = false;
                }
            }
        }

        #endregion

    }
}



